/***************************************************************************
 *   Copyright (C) 2012 by Mathias Paulin                                  *
 *   Mathias.Paulin@irit.fr                                                *
 *                                                                         *
 *   This program is free software; you can redistribute it and/or modify  *
 *   it under the terms of the GNU General Public License as published by  *
 *   the Free Software Foundation; either version 2 of the License, or     *
 *   (at your option) any later version.                                   *
 *                                                                         *
 *   This program is distributed in the hope that it will be useful,       *
 *   but WITHOUT ANY WARRANTY; without even the implied warranty of        *
 *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the         *
 *   GNU General Public License for more details.                          *
 *                                                                         *
 *   You should have received a copy of the GNU General Public License     *
 *   along with this program; if not, write to the                         *
 *   Free Software Foundation, Inc.,                                       *
 *   59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.             *
 ***************************************************************************/
#include "renderer.h"

#include "objloader.h"
#include "fileloader.h"

#include "rbo.h"



#include "glmaterial.h"
#include "parametricmeshes.h"

#include "bsplinepatch.h"

#include <glm/gtc/matrix_access.hpp>

namespace rendersystem {
    MyGLLinesMesh *controlPolygon;

    //-------------------------------------------
    /** @defgroup RendererGlobalFunctions
 * @author Mathias Paulin <Mathias.Paulin@irit.fr>
 * @ingroup RenderSystem
 * Global functions for the renderer.
 */
    //-------------------------------------------

    /**
 * @ingroup RendererGlobalFunctions
 * Print to stderr compilation log of a shader
 */
    void printShaderInfoLog(GLint shader)
    {
        int infoLogLen = 0;
        int charsWritten = 0;
        GLchar *infoLog;
        glAssert(glGetShaderiv(shader, GL_INFO_LOG_LENGTH, &infoLogLen));
        if (infoLogLen > 0) {
            infoLog = new GLchar[infoLogLen];
            // error check for fail to allocate memory omitted
            glAssert(glGetShaderInfoLog(shader, infoLogLen, &charsWritten, infoLog));
            std::cerr << "InfoLog:" << std::endl << infoLog << std::endl;
            delete [] infoLog;
        }
    }

    /**
 * @ingroup RendererGlobalFunctions
 * Print to stderr link log of a program
 */
    void printProgramInfoLog(GLint program)
    {
        int infoLogLen = 0;
        int charsWritten = 0;
        GLchar *infoLog;
        glAssert(glGetProgramiv(program, GL_INFO_LOG_LENGTH, &infoLogLen));
        if (infoLogLen > 0) {
            infoLog = new GLchar[infoLogLen];
            // error check for fail to allocate memory omitted
            glAssert(glGetProgramInfoLog(program, infoLogLen, &charsWritten, infoLog));
            std::cerr << "InfoLog:" << std::endl << infoLog << std::endl;
            delete [] infoLog;
        }
    }



    //-------------------------------------------
    // Méthodes de Renderer
    //-------------------------------------------

    //-------------------------------------------
    // Initialisation des resources globales
    //-------------------------------------------
    void Renderer::initRessources(){

        // Initialisation des paramètres OpenGL.

            // Début du code à écrire
            // 1 - Activer le test de profondeur
            glAssert( glEnable(GL_DEPTH_TEST) );
            // 2 - Positionner le mode de dessin
            glAssert( glPolygonMode(GL_FRONT_AND_BACK, GL_FILL) );
            // Fin du code à écrire


        // Viewing parameters initialisation
        initView();

        // Loading or building geometric data.
        initGeometry();

        // Set up lighting
        initLighting();

        // Shaders initialisation
        initShaders();

        //FBO initilisation
        initFbo();
    }

    //-------------------------------------------
    // Initialisation des paramètres de vue.
    //-------------------------------------------
    void Renderer::initView(){
        // Début du code à écrire
        // Initialiser la matrice de vue g_viewMatrix à partir des indications :
        // position de vue : 1.f, 0.f, 0.f
        // point visé : 0.0f, 0.0f, 0.0f
        // vecteur up : 0.0f, 1.0f, 0.0f
        // utiliser la fonction glm::lookAt pour calculer la matrice de vue correspondant
        glm::vec3 eye(1.f, 0.25f, 1.f);
        glm::vec3 center(0.0f, 0.0f, 0.0f);
        glm::vec3 up(0.0f, 1.0f, 0.0f);
        g_viewMatrix = glm::lookAt(eye,center,up);
        // Fin du code à écrire
    }



//-------------------------------------------
// Chargement et compilation des données géométriques de l'application.
//-------------------------------------------
//#define SHOW_FRAME
void Renderer::initGeometry(){
    // creation de la texture par defaut
    MyGLMaterial *material = new MyGLMaterial(glm::vec3(1.f, 1.f, 1.f), glm::vec3(1.0f, 1.0f, 1.0f), 25.f);
    Texture *defaultTex = new Texture("../data/default.jpg",GL_TEXTURE_2D);
    defaultTex->useMipMap(GL_NEAREST_MIPMAP_LINEAR, GL_LINEAR);
    material->setTexture(defaultTex);
    material->name="default";
    //ajout dans g_mat
    g_mat.push_back(material);



    //MyGLMaterial *defmaterial = material;


    //creation du vecteur de recuperation des materiaux
    std::vector<loaders::Material*> objectsMtl; //liste des materials de toute la scene
    //ajout d'un material pour garder la correspondance entre g_mat et celui ci
    loaders::Material *def = new loaders::Material();
    def->name="default_material_by_init";
    objectsMtl.push_back(def);





        // Génération des surfaces paramétriques

        std::vector<glm::vec3> controlMesh;
        controlMesh.push_back(glm::vec3(0.f, 0.f, 0.f));
        controlMesh.push_back(glm::vec3(0.f, 1.f, 0.f));
        controlMesh.push_back(glm::vec3(0.f, 2.f, 0.f));
        controlMesh.push_back(glm::vec3(0.f, 3.f, 0.f));
        controlMesh.push_back(glm::vec3(0.f, 3.5f, 0.f));

        controlMesh.push_back(glm::vec3(1.f, 0.f, 0.f));
        controlMesh.push_back(glm::vec3(1.f, 1.f, 1.7f));
        controlMesh.push_back(glm::vec3(1.f, 2.f, 1.f));
        controlMesh.push_back(glm::vec3(1.f, 3.f, 0.f));
        controlMesh.push_back(glm::vec3(1.f, 4.f, 0.f));

        controlMesh.push_back(glm::vec3(2.f, 0.f, 1.f));
        controlMesh.push_back(glm::vec3(2.f, 1.f, 1.6f));
        controlMesh.push_back(glm::vec3(2.f, 2.f, 1.f));
        controlMesh.push_back(glm::vec3(2.f, 3.f, 1.f));
        controlMesh.push_back(glm::vec3(2.f, 4.f, 0.f));

        controlMesh.push_back(glm::vec3(3.f, 0.f, 2.f));
        controlMesh.push_back(glm::vec3(3.f, 1.f, 1.4f));
        controlMesh.push_back(glm::vec3(3.f, 2.f, 2.6f));
        controlMesh.push_back(glm::vec3(3.f, 3.f, 1.f));
        controlMesh.push_back(glm::vec3(3.f, 3.5f, 0.f));


        controlMesh.push_back(glm::vec3(4.f, 0.f, 2.f));
        controlMesh.push_back(glm::vec3(4.f, 1.f, 1.4f));
        controlMesh.push_back(glm::vec3(4.f, 2.f, 1.6f));
        controlMesh.push_back(glm::vec3(4.f, 3.f, 1.f));
        controlMesh.push_back(glm::vec3(4.f, 2.f, 0.f));


        BSplinePatch *patch = new BSplinePatch(4, ParametricBSplinePatch::OpenUniform, controlMesh, 5); ////4
        ParametricMesh* paramPatch   = new ParametricBSplinePatch(patch);

        ParametricMesh* plane   = new ParametricMesh();
        ParametricMesh* sphere = new ParametricSphere();
        ParametricMesh* cylinder = new ParametricCylindre();
        ParametricMesh* cone   = new ParametricCone();



    paramPatch->generateMesh( 32, 32);
    plane->generateMesh(10, 10 );
    sphere->generateMesh(20,  20 );
    cylinder->generateMesh(20, 10);
    cone->generateMesh(20, 10);

    MyGLMesh *glPatch = new MyGLMesh(*paramPatch);
    MyGLMesh *glPlane = new MyGLMesh(*plane);
    MyGLMesh *glSphere = new MyGLMesh(*sphere);
    MyGLMesh *glCylinder = new MyGLMesh(*cylinder);
    MyGLMesh *glCone = new MyGLMesh(*cone);
    //g_meshes.push_back( glPatch);
    //g_meshes.push_back( glPlane );
    //g_meshes.push_back( glSphere );
    //g_meshes.push_back( glCylinder );
    //g_meshes.push_back( glCone );
    delete plane;
    delete sphere;
    delete cylinder;
    delete cone;
    delete paramPatch;


    // Un plan
    glm::mat4 theTransformation(1.0f);
    {
        theTransformation = glm::rotate(theTransformation, -90.0f, glm::vec3(1.f,0.f,0.f));
        theTransformation = glm::scale(theTransformation, glm::vec3(10.f,10.f,1.f));
        theTransformation = glm::translate(theTransformation, glm::vec3(0.f,0.f, -0.25f));
        //        MyGlEntity * newEntity = new MyGlEntity(glPlane, defaultMaterial, theTransformation);
        //        g_entities.push_back(newEntity);
        theTransformation = glm::mat4(1.0f);
        theTransformation = glm::rotate(theTransformation, -90.0f, glm::vec3(1.f,0.f,0.f));
        theTransformation = glm::scale(theTransformation, glm::vec3(1.f,1.f,.5f));
        theTransformation = glm::translate(theTransformation, glm::vec3(-2.f,-2.f, -1.5f));
        //
        MyGlEntity * newEntity = new MyGlEntity(glPatch, material, theTransformation);
        controlPolygon = new MyGLLinesMesh(theTransformation, patch);
        controlPolygon->compileGL();

        //g_entities.push_back(newEntity);
    }

    delete patch;


/*
#ifdef SHOW_FRAME

        // Construction du repère

        /*   theTransformation = glm::scale(glm::mat4(1.0f), glm::vec3(1.f,1.f,1.f));

        // La sphère pour noter l'Origine
        MyGLMaterial *centerMaterial = new MyGLMaterial(glm::vec3(1.f, 1.f, 0.01f), glm::vec3(1.0f, 1.0f, 1.0f), 25.f);
        unsigned char yellow[3];
        yellow[0]=yellow[1]=255;
        yellow[2]=2;
        Texture *centerTex = new Texture(yellow);
        centerMaterial->setTexture(centerTex);

        glm::mat4 sphereTransformation = glm::scale(theTransformation, glm::vec3(0.1f,0.1f,0.1f));
        MyGlEntity * newEntity = new MyGlEntity(glSphere, centerMaterial, sphereTransformation);
        g_entities.push_back(newEntity);

        // axe Z
        {
                MyGLMaterial *zAxisMaterial = new MyGLMaterial(glm::vec3(0.01f, 0.01f, 1.0f), glm::vec3(1.f, 1.f, 1.f), 50.f);

                unsigned char blue[3];
                blue[0]=blue[1]=2;
                blue[2]=255;
                Texture *theTex = new Texture(blue);
                zAxisMaterial->setTexture(theTex);

                glm::mat4 cylinderTransformation = glm::scale(theTransformation, glm::vec3(0.03f, 0.03f, 0.9f));
                newEntity = new MyGlEntity(glCylinder, zAxisMaterial, cylinderTransformation);
                g_entities.push_back(newEntity);

                glm::mat4 coneTransformation = glm::rotate(theTransformation, 180.0f, glm::vec3(0.f, 1.f, 0.f));
                coneTransformation = glm::scale(coneTransformation, glm::vec3(0.06f,0.06f,0.2f));
                coneTransformation = glm::translate(coneTransformation, glm::vec3(0.f,0.f,-5.f));

                newEntity = new MyGlEntity(glCone, zAxisMaterial, coneTransformation);
                g_entities.push_back(newEntity);
        }

        // axe X
        theTransformation = glm::rotate(theTransformation, 90.0f, glm::vec3(0.f, 1.f, 0.f));
        {
                MyGLMaterial *xAxisMaterial = new MyGLMaterial(glm::vec3(1.0f, 0.01f, 0.01f), glm::vec3(1.f, 1.f, 1.f), 50.f);

                unsigned char red[3];
                red[2]=red[1]=2;
                red[0]=255;
                Texture *theTex = new Texture(red);
                xAxisMaterial->setTexture(theTex);

                glm::mat4 cylinderTransformation = glm::scale(theTransformation, glm::vec3(0.03f, 0.03f, 0.9f));
                newEntity = new MyGlEntity(glCylinder, xAxisMaterial, cylinderTransformation);
                g_entities.push_back(newEntity);

                glm::mat4 coneTransformation = glm::rotate(theTransformation, 180.0f, glm::vec3(0.f, 1.f, 0.f));
                coneTransformation = glm::scale(coneTransformation, glm::vec3(0.06f,0.06f,0.2f));
                coneTransformation = glm::translate(coneTransformation, glm::vec3(0.f,0.f,-5.f));

                newEntity = new MyGlEntity(glCone, xAxisMaterial, coneTransformation);
                g_entities.push_back(newEntity);
        }

        // axe Y
        theTransformation = glm::rotate(theTransformation, -90.0f, glm::vec3(1.f, 0.f, 0.f));
        {
                MyGLMaterial *yAxisMaterial = new MyGLMaterial(glm::vec3(0.01f, 1.f, 0.01f), glm::vec3(1.f, 1.f, 1.f), 50.f);

                unsigned char green[3];
                green[0]=green[2]=2;
                green[1]=255;
                Texture *theTex = new Texture(green);
                yAxisMaterial->setTexture(theTex);

                glm::mat4 cylinderTransformation = glm::scale(theTransformation, glm::vec3(0.03f, 0.03f, 0.9f));
                newEntity = new MyGlEntity(glCylinder, yAxisMaterial, cylinderTransformation);
                g_entities.push_back(newEntity);

                glm::mat4 coneTransformation = glm::rotate(theTransformation, 180.0f, glm::vec3(0.f, 1.f, 0.f));
                coneTransformation = glm::scale(coneTransformation, glm::vec3(0.06f,0.06f,0.2f));
                coneTransformation = glm::translate(coneTransformation, glm::vec3(0.f,0.f,-5.f));

                newEntity = new MyGlEntity(glCone, yAxisMaterial, coneTransformation);
                g_entities.push_back(newEntity);
        }


#endif
*/


#define SHOW_OBJECTS
#ifdef SHOW_OBJECTS

        // Début du code à écrire
        // 1 - Construire un vecteur (std::vector<loaders::Mesh*>) de composantes géométriques (des maillages) à partir du fichier "../data/camel.obj"
        //  Créer et utiliser un objet de type loaders::obj_mtl::ObjLoader.
        // Utiliser la méthode load() pour charger le fichier. Si une erreur est intervenue pendant le chargement, afficher cette erreur.
        // S'il n'y a pas d'erreur, utiliser la méthode getObjects() sur l'objet de type loaders::obj_mtl::ObjLoader pour récupérer l'ensemble
        // des maillages chargés dans un vecteur de type std::vector<loaders::Mesh*>
        std::vector<loaders::Mesh*> objects;
        loaders::obj_mtl::ObjLoader *loader;
        QString reason;



    //Dromadaire
    loader = new loaders::obj_mtl::ObjLoader;
    //if (!loader->load("../data/camel.obj",reason))
    if (!loader->load("../data/scene03-09.obj",reason))
        std::cerr << "File not loaded. Reason : " << std::endl << reason.toStdString() << std::endl;
    else
        loader->getObjects(objects,objectsMtl);



    // 2 - Transformer ces maillages en maillages affichables (stockés dans la variable globale g_entities)
    //MyGLMaterial *camelMaterial = new MyGLMaterial(glm::vec3(0.8f, 0.7f, 0.5f), glm::vec3(1.0f, 1.0f, 1.0f), 25.f);
    //Texture *camelTex = new Texture("../data/diffus.png");
    //camelTex->useMipMap(GL_NEAREST_MIPMAP_LINEAR, GL_LINEAR);
    //camelMaterial->setTexture(camelTex);


    //Generer les glmat a partir d'objectsMtl
    for(int i=1;i<objectsMtl.size();i++){
        material = new MyGLMaterial(*objectsMtl[i]);
        g_mat.push_back(material);
        delete (objectsMtl[i]);
    }
    std::cerr<<std::endl<<"mat nbr : "<<g_mat.size()<<std::endl;



    //for(std::vector<loaders::Mesh*>::iterator it = objects.begin(); it != objects.end(); ++it ) {
    for(int i=0;i<objects.size();i++){
        //std::cerr<<std::endl;
        //MyGLMesh *newObject = new MyGLMesh(*(*it));
        loaders::Mesh *tmp=objects[i];
        MyGLMesh *newObject = new MyGLMesh(*tmp);
        //std::cerr<<"new object mat id : "<<newObject->matId<<std::endl;
        newObject->matId=tmp->getMaterialId();
        //std::cerr<<"new object mat id is now : "<<newObject->matId<<std::endl;
        g_meshes.push_back( newObject );
        glm::mat4 theTransformation(1.0f);
        theTransformation = glm::translate(theTransformation, glm::vec3(0.f,0.f,0.f));
        // scale to fit in view frustum
        theTransformation = glm::scale(theTransformation, glm::vec3(0.005f, 0.005f, 0.005f));
        //std::cerr<<std::endl<<"material id : "<<newObject->materialId<<" ; "<<std::endl;
        //std::cerr<<"material id : "<<newObject->matId<<" ; "<<std::endl;
        //std::cerr<<"material texture : "<<getMaterialById(newObject->getMaterialId())->name<<" ; "<<std::endl;
        MyGlEntity * newEntity = new MyGlEntity(newObject,getMaterialById(newObject->matId),theTransformation);
        //MyGlEntity * newEntity = new MyGlEntity(newObject,material,theTransformation);
        g_entities.push_back(newEntity);



        //delete (*it);
    }
    delete loader;
/*
//Poubelle
    std::vector<loaders::Mesh*> trashbin;
    std::vector<loaders::Material*> trashMtl;

    loader = new loaders::obj_mtl::ObjLoader;
    if (!loader->load("../data/trashbin.obj",reason))
        std::cerr << "File not loaded. Reason : " << std::endl << reason.toStdString() << std::endl;
    else
        loader->getObjects(trashbin,trashMtl);



    MyGLMaterial *trashMaterial = new MyGLMaterial(glm::vec3(1.0f, 1.0f, 1.0f), glm::vec3(1.0f, 1.0f, 1.0f), 25.f);
    Texture *trashTex = new Texture("../data/archinterior9_03_foresta.jpg");
    trashTex->useMipMap(GL_NEAREST_MIPMAP_LINEAR, GL_LINEAR);
    trashMaterial->setTexture(trashTex);


        for (std::vector<loaders::Mesh*>::iterator it = trashbin.begin(); it != trashbin.end(); ++it ) {
            MyGLMesh *newObject = new MyGLMesh(*(*it));
            g_meshes.push_back( newObject );
            for(int i=0; i<5;i++){
                glm::mat4 theTransformation(1.f);

                theTransformation = glm::translate(theTransformation, glm::vec3((-1.0f+(float)i),-0.2f,.5f));
                theTransformation = glm::scale(theTransformation, glm::vec3(0.005f, 0.005f, 0.005f));


                MyGlEntity * newEntity = new MyGlEntity(newObject, trashMaterial, theTransformation);
                g_entities.push_back(newEntity);
            }
            delete (*it);
        }

    delete loader;
*/
    /*

    //Colonnes
    std::vector<loaders::Mesh*> column;
    loader = new loaders::obj_mtl::ObjLoader;

        if (!loader->load("../data/column.obj",reason))
               std::cerr << "File not loaded. Reason : " << std::endl << reason.toStdString() << std::endl;
           else
               loader->getObjects(column);

        MyGLMaterial *columnMaterial = new MyGLMaterial(glm::vec3(0.5f, 0.5f, 0.5f), glm::vec3(1.0f, -0.2f, 1.0f), 25.f);

          Texture *diffuseTex = new Texture("../data/column_diffuse.png");
      columnMaterial->setTexture(diffuseTex);
        //  diffuseTex->useMipMap(GL_NEAREST_MIPMAP_LINEAR, GL_LINEAR);

      //  columnMaterial->setdiffuseTexture(diffuseTex);

        Texture *specularTex = new Texture("../data/column_specular.png");
        specularTex->useMipMap(GL_NEAREST_MIPMAP_LINEAR, GL_LINEAR);
        columnMaterial->setspecularTexture(specularTex);

        Texture *shininessTex = new Texture("../data/column_shininess.png");
        shininessTex->useMipMap(GL_NEAREST_MIPMAP_LINEAR, GL_LINEAR);
        columnMaterial->setshininessTexture(shininessTex);

        for (std::vector<loaders::Mesh*>::iterator it = objects.begin(); it != objects.end(); ++it ) {
              MyGLMesh *newObject = new MyGLMesh(*(*it));
              g_meshes.push_back( newObject );
              float pas=2.0f;
              for(int i=0;i<5;i++){
              glm::mat4 theTransformation(1.0f);{
                theTransformation = glm::translate(theTransformation, glm::vec3(-4.0f+(i*pas),0.04f,-4.f));
                }

             MyGlEntity * newEntity = new MyGlEntity(newObject, columnMaterial, theTransformation);
             g_entities.push_back(newEntity);

              }
              delete (*it);
          }


          delete loader;
*/

#endif
      // 3 - Compiler les maillage pour préparer leur affichage en openGL.
      for (std::vector<MyGLMesh*>::iterator it = g_meshes.begin(); it != g_meshes.end(); ++it ) {
          (*it)->compileGL();
      }


      g_polyline.pushPoint(glm::vec3(0,0,0));
      g_polyline.pushPoint(glm::vec3(0,0,0));
      g_polyline.compileGL();
      // Fin du code à écrire
  }



/*
      void Renderer::initGeometry(){
          // Début du code à écrire
          // 1 - Construire un vecteur (std::vector<loaders::Mesh*>) de composantes géométriques (des maillages) à partir du fichier "../data/camel.obj"
          //  Créer et utiliser un objet de type loaders::obj_mtl::ObjLoader.
          // Utiliser la méthode load() pour charger le fichier. Si une erreur est intervenue pendant le chargement, afficher cette erreur.
          // S'il n'y a pas d'erreur, utiliser la méthode getObjects() sur l'objet de type loaders::obj_mtl::ObjLoader pour récupérer l'ensemble
          // des maillages chargés dans un vecteur de type std::vector<loaders::Mesh*>


              std::vector<loaders::Mesh*> objects;
          loaders::obj_mtl::ObjLoader loader;
          QString reason;
          if (!loader.load("../data/column.obj",reason))
                 std::cerr << "File not loaded. Reason : " << std::endl << reason.toStdString() << std::endl;
             else
                 loader.getObjects(objects);

          // 2 - Transformer ces maillages en maillages affichables (stockés dans la variable globale g_entities)
        //ligne origininale:  MyGLMaterial *columnMaterial = new MyGLMaterial(glm::vec3(0.8f, 0.8f, 0.8f), glm::vec3(1.0f, 1.0f, 1.0f), 25.f);
          MyGLMaterial *columnMaterial = new MyGLMaterial(glm::vec3(0.5f, 0.5f, 0.5f), glm::vec3(1.0f, 1.0f, 1.0f), 25.f);
          Texture *diffuseTex = new Texture("../data/column_diffuse.png",GL_TEXTURE_2D);
          diffuseTex->useMipMap(GL_NEAREST_MIPMAP_LINEAR, GL_LINEAR);
          columnMaterial->setdiffuseTexture(diffuseTex);

          Texture *specularTex = new Texture("../data/column_specular.png",GL_TEXTURE_2D);
          specularTex->useMipMap(GL_NEAREST_MIPMAP_LINEAR, GL_LINEAR);
          columnMaterial->setspecularTexture(specularTex);

          Texture *shininessTex = new Texture("../data/column_shininess.png",GL_TEXTURE_2D);
          shininessTex->useMipMap(GL_NEAREST_MIPMAP_LINEAR, GL_LINEAR);
          columnMaterial->setshininessTexture(shininessTex);

          Texture *defaultMapTex = new Texture("../data/blanc.jpg",GL_TEXTURE_2D);
          defaultMapTex->useMipMap(GL_NEAREST_MIPMAP_LINEAR, GL_LINEAR);
             columnMaterial->setmapTexture(defaultMapTex);

       /* Test de modification des valeurs diffuse specular and shiness
        *   MyGLMaterial *columnMaterial1 = new MyGLMaterial(glm::vec3(0.8f, 0.8f, 0.8f), glm::vec3(1.0f, 1.0f, 1.0f), 25.f);
          //  diffuseTex = new Texture("../data/column_diffuse.png");
            diffuseTex->useMipMap(GL_NEAREST_MIPMAP_LINEAR, GL_LINEAR);
            columnMaterial1->setdiffuseTexture(diffuseTex);

          //  specularTex = new Texture("../data/column_specular.png");
            specularTex->useMipMap(GL_NEAREST_MIPMAP_LINEAR, GL_LINEAR);
            columnMaterial1->setspecularTexture(specularTex);

           //  shininessTex = new Texture("../data/column_shininess.png");
            shininessTex->useMipMap(GL_NEAREST_MIPMAP_LINEAR, GL_LINEAR);
            columnMaterial1->setshininessTexture(shininessTex);
      */

    /*      for (std::vector<loaders::Mesh*>::iterator it = objects.begin(); it != objects.end(); ++it ) {
              MyGLMesh *newObject = new MyGLMesh(*(*it));
              g_meshes.push_back( newObject );
              float pas=2.0f;
              for(int i=0;i<5;i++){
              glm::mat4 theTransformation(1.0f);{
                      theTransformation = glm::translate(theTransformation, glm::vec3(-4.0f+(i*pas),0.04f,-4.f));
              }

             MyGlEntity * newEntity = new MyGlEntity(newObject, columnMaterial, theTransformation);
             g_entities.push_back(newEntity);

              }
              delete (*it);
          }





          std::vector<loaders::Mesh*> camelObjects;
          loaders::obj_mtl::ObjLoader cloader;
         // QString reason;
          if (!cloader.load("../data/camel.obj",reason))
                std::cerr << "File not loaded. Reason : " << std::endl << reason.toStdString() << std::endl;
            else
                cloader.getObjects(camelObjects);


          MyGLMaterial *camelMaterial = new MyGLMaterial(glm::vec3(0.5f, 0.5f, 0.5f), glm::vec3(1.0f, 1.0f, 1.0f), 5.f);
          Texture *ddefaultTex = new Texture("../data/diffus.png",GL_TEXTURE_2D);
          ddefaultTex->useMipMap(GL_NEAREST_MIPMAP_LINEAR, GL_LINEAR);
          camelMaterial->setdiffuseTexture(ddefaultTex);
          camelMaterial->setspecularTexture(ddefaultTex);
          camelMaterial->setshininessTexture(ddefaultTex);
          camelMaterial->setmapTexture(defaultMapTex);


          for (std::vector<loaders::Mesh*>::iterator it = camelObjects.begin(); it != camelObjects.end(); ++it ) {
                 MyGLMesh *newObject = new MyGLMesh(*(*it));
                 g_meshes.push_back( newObject );
                 glm::mat4 theTransformation(1.0f);{
                      theTransformation = glm::translate(theTransformation, glm::vec3(-3.f,0.5f,-3.f));
              }

                MyGlEntity * cnewEntity = new MyGlEntity(newObject, camelMaterial, theTransformation);
                g_entities.push_back(cnewEntity);
                 delete (*it);
             }


          std::vector<loaders::Mesh*> pitcherObjects;
          loaders::obj_mtl::ObjLoader pitcherloader;
         // QString reason;
          if (!pitcherloader.load("../data/pitcher.obj",reason))
                std::cerr << "File not loaded. Reason : " << std::endl << reason.toStdString() << std::endl;
            else
                pitcherloader.getObjects(pitcherObjects);


          MyGLMaterial *pitcherMaterial = new MyGLMaterial(glm::vec3(0.8f, 0.8f, 0.8f), glm::vec3(1.0f, 1.0f, 1.0f), 100.f);
          Texture *pitcherTex = new Texture("../data/Patterned_Fabric16.jpg",GL_TEXTURE_2D);
          pitcherTex->useMipMap(GL_NEAREST_MIPMAP_LINEAR, GL_LINEAR);
          pitcherMaterial->setdiffuseTexture(pitcherTex);
          pitcherMaterial->setspecularTexture(pitcherTex);
          pitcherMaterial->setshininessTexture(pitcherTex);
       pitcherMaterial->setmapTexture(defaultMapTex);


          for (std::vector<loaders::Mesh*>::iterator it = pitcherObjects.begin(); it != pitcherObjects.end(); ++it ) {
                 MyGLMesh *newObject = new MyGLMesh(*(*it));
                 g_meshes.push_back( newObject );
                 glm::mat4 theTransformation(1.0f);{
                      theTransformation = glm::translate(theTransformation, glm::vec3(1.f,0.0f,-2.f));
                      theTransformation = glm::scale(theTransformation, glm::vec3(0.03f,0.03f,0.03f));
              }

                MyGlEntity * pnewEntity = new MyGlEntity(newObject, pitcherMaterial, theTransformation);
                g_entities.push_back(pnewEntity);
                 delete (*it);
             }






          MyGLMaterial *defaultMaterial = new MyGLMaterial(glm::vec3(0.8f, 0.8f, 0.8f), glm::vec3(1.0f, 1.0f, 1.0f), 1.f);
          Texture *defaultTex = new Texture("../data/EarlyTablet.jpg",GL_TEXTURE_2D);
          defaultTex->useMipMap(GL_NEAREST_MIPMAP_LINEAR, GL_LINEAR);
          Texture *mapTex = new Texture("../data/EarlyTabletBump2.jpg",GL_TEXTURE_2D);
          mapTex->useMipMap(GL_NEAREST_MIPMAP_LINEAR, GL_LINEAR);
          defaultMaterial->setdiffuseTexture(defaultTex);
          defaultMaterial->setspecularTexture(defaultTex);
          defaultMaterial->setshininessTexture(defaultTex);
          defaultMaterial->setmapTexture(mapTex);



              /*
          //
          //TP3
          //
          // Génération des surfaces paramétriques
          ParametricMesh* plane   = new ParametricMesh();
          ParametricMesh* sphere = new ParametricSphere();
          ParametricMesh* cylinder    = new ParametricCylindre();
          ParametricMesh* cone   = new ParametricCone();

          plane->generateMesh(4,  4);
          sphere->generateMesh(50,  25 );
          cylinder->generateMesh(100, 100);
          cone->generateMesh(100, 100);


*/



 //         MyGLMesh *glPlane = new MyGLMesh(*plane);
       //   MyGLMesh *glSphere = new MyGLMesh(*sphere);
        //  MyGLMesh *glCylinder = new MyGLMesh(*cylinder);
        //  MyGLMesh *glCone = new MyGLMesh(*cone);
  //        quad_meshes.push_back(glPlane);
         // g_meshes.push_back( glPlane );
        //  g_meshes.push_back( glSphere );
        //  g_meshes.push_back( glCylinder );
         // g_meshes.push_back( glCone );
    //      delete plane;
          //delete sphere;
          //delete cylinder;
          //delete cone;


          // Un plan

          /*

          glm::mat4 theTransformation(1.0f);
           {
              theTransformation = glm::rotate(theTransformation, -90.0f, glm::vec3(1.f,0.f,0.f));
              theTransformation = glm::scale(theTransformation, glm::vec3(10.f,10.f,1.f));
              theTransformation = glm::translate(theTransformation, glm::vec3(0.f,0.f,0.00f));

              MyGlEntity * newEntity = new MyGlEntity(glPlane, defaultMaterial, theTransformation);
              quad_entities.push_back(newEntity);
          }
*/

         /* // Construction du repère

          theTransformation = glm::scale(glm::mat4(1.0f), glm::vec3(1.f,1.f,1.f));

          // La sphère pour noter l'Origine
          MyGLMaterial *centerMaterial = new MyGLMaterial(glm::vec3(1.f, 1.f, 0.01f), glm::vec3(1.0f, 1.0f, 1.0f), 25.f);
          unsigned char yellow[3];
          yellow[0]=yellow[1]=255;
          yellow[2]=2;
          Texture *centerTex = new Texture(yellow);
          centerMaterial->setTexture(centerTex);

          glm::mat4 sphereTransformation = glm::scale(theTransformation, glm::vec3(0.1f,0.1f,0.1f));
          MyGlEntity * newEntity = new MyGlEntity(glSphere, centerMaterial, sphereTransformation);
          g_entities.push_back(newEntity);

          // axe Z
          {
              MyGLMaterial *zAxisMaterial = new MyGLMaterial(glm::vec3(0.01f, 0.01f, 1.0f), glm::vec3(1.f, 1.f, 1.f), 50.f);

              unsigned char blue[3];
              blue[0]=blue[1]=2;
              blue[2]=255;
              Texture *theTex = new Texture(blue);
              zAxisMaterial->setTexture(theTex);

              glm::mat4 cylinderTransformation = glm::scale(theTransformation, glm::vec3(0.03f, 0.03f, 0.9f));
              newEntity = new MyGlEntity(glCylinder, zAxisMaterial, cylinderTransformation);
              g_entities.push_back(newEntity);

              glm::mat4 coneTransformation = glm::rotate(theTransformation, 180.0f, glm::vec3(0.f, 1.f, 0.f));
              coneTransformation = glm::scale(coneTransformation, glm::vec3(0.06f,0.06f,0.2f));
              coneTransformation = glm::translate(coneTransformation, glm::vec3(0.f,0.f,-5.f));

              newEntity = new MyGlEntity(glCone, zAxisMaterial, coneTransformation);
              g_entities.push_back(newEntity);
          }

          // axe X
          theTransformation = glm::rotate(theTransformation, 90.0f, glm::vec3(0.f, 1.f, 0.f));
          {
              MyGLMaterial *xAxisMaterial = new MyGLMaterial(glm::vec3(1.0f, 0.01f, 0.01f), glm::vec3(1.f, 1.f, 1.f), 50.f);

              unsigned char red[3];
              red[2]=red[1]=2;
              red[0]=255;
              Texture *theTex = new Texture(red);
              xAxisMaterial->setTexture(theTex);

              glm::mat4 cylinderTransformation = glm::scale(theTransformation, glm::vec3(0.03f, 0.03f, 0.9f));
              newEntity = new MyGlEntity(glCylinder, xAxisMaterial, cylinderTransformation);
              g_entities.push_back(newEntity);

              glm::mat4 coneTransformation = glm::rotate(theTransformation, 180.0f, glm::vec3(0.f, 1.f, 0.f));
              coneTransformation = glm::scale(coneTransformation, glm::vec3(0.06f,0.06f,0.2f));
              coneTransformation = glm::translate(coneTransformation, glm::vec3(0.f,0.f,-5.f));

              newEntity = new MyGlEntity(glCone, xAxisMaterial, coneTransformation);
              g_entities.push_back(newEntity);
          }

          // axe Y
          theTransformation = glm::rotate(theTransformation, -90.0f, glm::vec3(1.f, 0.f, 0.f));
          {
              MyGLMaterial *yAxisMaterial = new MyGLMaterial(glm::vec3(0.01f, 1.f, 0.01f), glm::vec3(1.f, 1.f, 1.f), 50.f);

              unsigned char green[3];
              green[0]=green[2]=2;
              green[1]=255;
              Texture *theTex = new Texture(green);
              yAxisMaterial->setTexture(theTex);

              glm::mat4 cylinderTransformation = glm::scale(theTransformation, glm::vec3(0.03f, 0.03f, 0.9f));
              newEntity = new MyGlEntity(glCylinder, yAxisMaterial, cylinderTransformation);
              g_entities.push_back(newEntity);

              glm::mat4 coneTransformation = glm::rotate(theTransformation, 180.0f, glm::vec3(0.f, 1.f, 0.f));
              coneTransformation = glm::scale(coneTransformation, glm::vec3(0.06f,0.06f,0.2f));
              coneTransformation = glm::translate(coneTransformation, glm::vec3(0.f,0.f,-5.f));

              newEntity = new MyGlEntity(glCone, yAxisMaterial, coneTransformation);
              g_entities.push_back(newEntity);
          }
      */
  /*
          // 3 - Compiler les maillage pour préparer leur affichage en openGL.
          for (std::vector<MyGLMesh*>::iterator it = g_meshes.begin(); it != g_meshes.end(); ++it ) {
              (*it)->compileGL();
          }


          // 3 - Compiler les maillage pour préparer leur affichage en openGL.
          for (std::vector<MyGLMesh*>::iterator it = quad_meshes.begin(); it != quad_meshes.end(); ++it ) {
              (*it)->compileGL();
          }

          // Fin du code à écrire
      }
*/



    //-------------------------------------------
    // Chargement et compilation des shaders
    //-------------------------------------------

    int Renderer::initShader(std::string vsFileName, std::string fsFileName){
        // Début du code à écrire
        // Les opérations devant être faites pour pouvoir utiliser des shaders OpenGL dans notre application sont les suivantes :
        // 1 - Vertex shader :
        //   1.1 - Charger le source depuis le fichier "../shaders/vertexdefault.glsl" en utilisant la fonction loaders::text::loadFile() de lecture d'un fichier texte
        char* vertexShaderSource = loaders::text::loadFile(vsFileName.c_str());
        //   1.2 - Créer un objet OpenGL VERTEX_SHADER et y associer le source
        int vertexShaderId;
        glAssert(vertexShaderId = glCreateShader(GL_VERTEX_SHADER));
        glAssert( glShaderSource(vertexShaderId, 1, (const GLchar **)&vertexShaderSource, NULL) );
        //   1.3 - Compiler le shader
        glAssert(glCompileShader(vertexShaderId));
        //   1.4 - Vérifier les erreurs de compilation
        GLint compiled;
        glAssert(glGetShaderiv(vertexShaderId, GL_COMPILE_STATUS, &compiled));
        if (!compiled) {
            std::cerr << "[" << vsFileName << "] Vertex shader not compiled : " << std::endl;
            printShaderInfoLog(vertexShaderId);
            return 0;
        }

        // 2 - Fragment shader :
        //   2.1 - Charger le source depuis le fichier "../shaders/fragmentdefault.glsl" en utilisant la fonction loaders::text::loadFile() de lecture d'un fichier texte
        char *fragmentShaderSource;
        fragmentShaderSource = loaders::text::loadFile(fsFileName.c_str());
        //   2.2 - Créer un objet OpenGL FRAGMENT_SHADER et y associer le source
        int fragmentShaderId;
        glAssert(fragmentShaderId = glCreateShader(GL_FRAGMENT_SHADER));
        glAssert( glShaderSource(fragmentShaderId, 1, (const GLchar **)&fragmentShaderSource, NULL) );
        //   2.3 - Compiler le shader
        glAssert(glCompileShader(fragmentShaderId));
        //   2.4 - Vérifier les erreurs de compilation
        glAssert(glGetShaderiv(fragmentShaderId, GL_COMPILE_STATUS, &compiled));
        if (!compiled) {
            std::cerr << "[" << fsFileName << "] fragment shader not compiled : " << std::endl;
            printShaderInfoLog(fragmentShaderId);
            return 0;
        }

        // 3 - Programme :
        //   3.1 - Créer un programme OpenGL, stocker son identifiant dans g_Program et y associer les shaders
        int program;
        glAssert(program = glCreateProgram());
        glAssert( glAttachShader(program, vertexShaderId) );
        glAssert( glAttachShader(program, fragmentShaderId) );
        //   3.2 - Fixer les positions des attributs géométriques en accord avec la classe MyGLMesh et les conventions
        //         d'écriture du source GLSL inPosition --> index 0, inNormal --> index 1 et inTexCoord --> index 2.

        glAssert(glBindAttribLocation(program, 0, "inPosition"));
        glAssert(glBindAttribLocation(program, 1, "inNormal"));
          glAssert(glBindAttribLocation(program,2,"inCoeffAo"));
        glAssert(glBindAttribLocation(program, 3, "inTexCoord"));

        //   3.3 - Lier le programme
        glAssert( glLinkProgram(program) );
        //   3.4 - Vérifier les erreurs d'édition de lien
        GLint linked;
        glAssert(glGetProgramiv(program, GL_LINK_STATUS, &linked));
        if (!linked) {
            std::cerr << "Program not linked" << std::endl;
            printProgramInfoLog(program);
        } glAssert( glUseProgram(g_progDepth) );
        return program;
        // Fin du code à écrire

    }

    void Renderer::initShaders(){
        g_program = initShader("../shaders/vertexdefault.glsl", "../shaders/fragmentdefault.glsl");
        g_programLine = initShader("../shaders/vertexline.glsl", "../shaders/fragmentline.glsl");
        g_progDepth = initShader("../shaders/vertexdepth.glsl", "../shaders/fragmentdepth.glsl");
       g_progAmb = initShader("../shaders/vertexamb.glsl","../shaders/fragmentamb.glsl");
       g_progObject = initShader("../shaders/vertexobject.glsl","../shaders/fragmantobject.glsl");
        // Fin du code à écrire
    }

    void Renderer::initFbo(){
        mFbo = new FBO(FBO::Components (FBO::DEPTH | FBO::COLOR), mWidth, mHeight);

        //Texture * mDepthMap = new Texture("depth", GL_TEXTURE_RECTANGLE);
        //  mDepthMap->initGL(GL_DEPTH_COMPONENT24, mWidth, mHeight, GL_DEPTH_COMPONENT, GL_UNSIGNED_BYTE, NULL);
        mRbo = new RBO(GL_DEPTH_COMPONENT);
        mRbo->setSize(100,100);



        mAmbientMap = new Texture("ambient", GL_TEXTURE_RECTANGLE);
          mAmbientMap->deleteGL(); //? etre sur qu'elle est vide?
          mAmbientMap->initGL(GL_RGBA32F, 100, 100, GL_RGBA, GL_UNSIGNED_BYTE, NULL);


        test = new Texture("../data/rouge.jpg",GL_TEXTURE_RECTANGLE);
        mNormalMap = new Texture("normal", GL_TEXTURE_RECTANGLE);
        mNormalMap->deleteGL(); //? etre sur qu'elle est vide?
        mNormalMap->initGL(GL_RGBA32F, 100, 100, GL_RGBA, GL_UNSIGNED_BYTE, NULL);

        mFbo->bind();
        mFbo->attachTexture(GL_COLOR_ATTACHMENT0, mNormalMap);
        mFbo->attachRBO(GL_DEPTH_ATTACHMENT,mRbo);//attachTexture(GL_DEPTH_ATTACHMENT, mDepthMap);
        mFbo->check();
        mFbo->unbind();
    }



    void Renderer::initLighting(){
        // Key light
        glm::vec3 keyLightPosition =  glm::vec3(-1.f, 0.5f, 0.f);
        glm::vec3 keyLightColor =   glm::vec3(0.6f, 0.7f, 0.8f); // Key light  test :glm::vec3(0.0f, 0.0f, 0.8f);

        // Fill light
        glm::vec3 fillLightPosition =  glm::vec3(1.f, 0.5f, 0.f);
        glm::vec3 fillLightColor = 0.75f * keyLightColor; //  test : glm::vec3(0.6f, 0.0f, 0.0f);

        // Back light
        glm::vec3 backLightPosition =  glm::vec3(-1.f, .5f, -2.f);
        glm::vec3 backLightColor =  0.75f * keyLightColor; // Back light test : glm::vec3(0.0f, 0.7f, 0.0f);

        MyGlLight *light;
        light = new MyGlLight(keyLightPosition, keyLightColor, true);
        g_lights.push_back(light);

        light = new MyGlLight(fillLightPosition, fillLightColor, true);
        g_lights.push_back(light);

        light = new MyGlLight(backLightPosition, backLightColor, false);
        g_lights.push_back(light);

    }

    //-------------------------------------------
    // Fonction générale de rendu
    //-------------------------------------------

    void  Renderer::render(const glm::mat4x4 &modelViewMatrix, const glm::mat4x4 &projectionMatrix){
        //FBO initilisation
        //	initFbo();
        // Début du code à écrire
        // Fonction principale de calcul d'une image, son rôle est de séquencer l'ensemble des ordres de dessin de l'API de bas niveau.
        // 1 -  Préparer de l'image pour le rendu :
        //    1.1 - Effacer les buffers de destination
        glAssert ( glClearColor ( 0.f, 0.3f, 0.3f, 0.f ) );
        glAssert ( glClear ( GL_COLOR_BUFFER_BIT|GL_DEPTH_BUFFER_BIT ) );

        // 2 - Construire les matrices de vue et de projection :
        //    2.1 - Définir une matrice de projection perspective
        glm::mat4x4 localprojectionMatrix = glm::perspective	( 90.f, (float)mWidth/(float)mHeight, 0.1f, 10.0f );
        //    2.2 - Définir la matrice de vue à partir de g_viewMatrix et de modelViewMatrix
        glm::mat4x4 localmodelViewMatrix = g_viewMatrix;




        //FBO depth buffer + Texture Normal,depthview
      mFbo->setSize(mWidth,mHeight);

       mFbo->bind();

//1er passage screen space
   /*    glAssert( glUseProgram(g_progDepth) );

        //    3.2 - Récupérer les adresses des paramètres
       GLint transformationLoc[4];
       glAssert( transformationLoc[0] = glGetUniformLocation(g_progDepth, "modelViewMatrix") );
        glAssert( transformationLoc[1] = glGetUniformLocation(g_progDepth, "projectionMatrix") );
        glAssert( transformationLoc[2] = glGetUniformLocation(g_progDepth, "normalMatrix") );
        glAssert( transformationLoc[3] = glGetUniformLocation(g_progDepth, "MVP") );


        GLint materialLoc[13];
*/

       //shader object space
       glAssert( glUseProgram(g_progObject) );

        //    3.2 - Récupérer les adresses des paramètres
       GLint transformationLoc[4];
       glAssert( transformationLoc[0] = glGetUniformLocation(g_progObject, "modelViewMatrix") );
        glAssert( transformationLoc[1] = glGetUniformLocation(g_progObject, "projectionMatrix") );
        glAssert( transformationLoc[2] = glGetUniformLocation(g_progObject, "normalMatrix") );
        glAssert( transformationLoc[3] = glGetUniformLocation(g_progObject, "MVP") );


        GLint materialLoc[9];



        //4 - Dessiner les objets de la scène :
        for (std::vector<MyGlEntity*>::iterator it = g_entities.begin(); it != g_entities.end(); ++it ) {
            (*it)->drawGL(localmodelViewMatrix, localprojectionMatrix, transformationLoc, materialLoc);
        }




       mFbo->detachTexture(GL_COLOR_ATTACHMENT0,Texture::TEX_2D);
     //glAssert(glFramebufferTexture2D(GL_FRAMEBUFFER,Texture::TEX_2D , 0, 0, 0));



    // mFbo->bind();


     //mAmbientMap->setSize(mWidth,mHeight);

   //      mFbo->attachTexture(GL_COLOR_ATTACHMENT0, mAmbientMap);
    //  mFbo->check();


      mFbo->unbind();
       glAssert(glBindFramebuffer(GL_FRAMEBUFFER, 0));




//Debut de code pour 2eme passage screen space
 /*      GLint mvpLoc[1];
       glAssert( mvpLoc[0] = glGetUniformLocation(g_progAmb, "MVP") );
       GLint textureLoc[1];
       glAssert( textureLoc[0] = glGetUniformLocation(g_progAmb, "textNormDepth") );





       //    2.1 - Définir une matrice de projection perspective
       glm::mat4x4 localorthogonalMatrix =glm::ortho(0.f,(float) mWidth, 0.f, (float)mHeight, -1.f, 1.f);
       //    2.2 - Définir la matrice de vue à partir de sceneToViewMatrix et de mTransformMatrix
     // localmodelViewMatrix =localmodelViewMatrix*mModelToSceneMatrix;

       //    2.4 - Calculer la matrice MVP de passage du repère objet au repère image
       glm::mat4x4 MVP = localorthogonalMatrix * localmodelViewMatrix;
       // 3 - Paramétrer les shader par défaut :
       //    3.1 - Positionner les paramètres de transformation

       glAssert( glUniformMatrix4fv( mvpLoc[0], 1, GL_FALSE, glm::value_ptr(MVP)));



*/


       /*
             for (std::vector<MyGlEntity*>::iterator it = quad_entities.begin(); it != quad_entities.end(); ++it ) {
                                 (*it)->drawGL(localmodelViewMatrix, localprojectionMatrix, transformationLoc, materialLoc);
                         }

       */

           //    mFbo->detachTexture(GL_COLOR_ATTACHMENT0,Texture::TEX_2D);

             //     mFbo->unbind();

                //  glAssert(glBindFramebuffer(GL_FRAMEBUFFER, 0));

       //mAmbientMap->bind(0);

       //Shader default multitexture
       /*

               // 3 - Activer et paramétrer les shader par défaut :
               //    3.1 - Activer le programme par défaut
               //     glAssert( glUseProgram(g_program) );
              glAssert( glUseProgram(g_program) );

               //    3.2 - Récupérer les adresses des paramètres
              // GLint transformationLoc[4];
               glAssert( transformationLoc[0] = glGetUniformLocation(g_program, "modelViewMatrix") );
               glAssert( transformationLoc[1] = glGetUniformLocation(g_program, "projectionMatrix") );
               glAssert( transformationLoc[2] = glGetUniformLocation(g_program, "normalMatrix") );
               glAssert( transformationLoc[3] = glGetUniformLocation(g_program, "MVP") );


            //   GLint materialLoc[9];
               glAssert( materialLoc[0] = glGetUniformLocation(g_program, "materialKd") );
               glAssert( materialLoc[1] = glGetUniformLocation(g_program, "materialKs") );
               glAssert( materialLoc[2] = glGetUniformLocation(g_program, "materialNs") );
             //  glAssert( materialLoc[3] = glGetUniformLocation(g_program, "baseTexture") );
               glAssert( materialLoc[3] = glGetUniformLocation(g_program, "diffuseTexture") );
               glAssert( materialLoc[4] = glGetUniformLocation(g_program, "specularTexture") );
               glAssert( materialLoc[5] = glGetUniformLocation(g_program, "shininessTexture") );
               glAssert( materialLoc[6] = glGetUniformLocation(g_program, "mapTexture") );

               glAssert( materialLoc[7] = glGetUniformLocation(g_program, "isMultiText") );




               glAssert( materialLoc[8] = glGetUniformLocation(g_program, "textNormDepth") );
*/

       mNormalMap->bind(0);
   // test->bind(0);
/*
//      glAssert( glUniform1i(textureLoc[0], 0) );
glAssert( glUniform1i(materialLoc[8], 0) );

      //texture normal,depth

        glAssert( glUniform3fv( glGetUniformLocation(g_program, "keyLightPosition"), 1, glm::value_ptr(g_lights[0]->getPosition())) );
        glAssert( glUniform3fv( glGetUniformLocation(g_program, "keyLightColor"), 1, glm::value_ptr(g_lights[0]->getColor())) );

        glAssert( glUniform3fv( glGetUniformLocation(g_program, "fillLightPosition"), 1, glm::value_ptr(g_lights[1]->getPosition())) );
        glAssert( glUniform3fv( glGetUniformLocation(g_program, "fillLightColor"), 1, glm::value_ptr(g_lights[1]->getColor())) );

        glAssert( glUniform3fv( glGetUniformLocation(g_program, "backLightPosition"), 1, glm::value_ptr(g_lights[2]->getPosition())) );
        glAssert( glUniform3fv( glGetUniformLocation(g_program, "backLightColor"), 1, glm::value_ptr(g_lights[2]->getColor())) );




*/


        // 4 - Dessiner les objets de la scène :

           for (std::vector<MyGlEntity*>::iterator it = g_entities.begin(); it != g_entities.end(); ++it ) {
                                (*it)->drawGL(localmodelViewMatrix, localprojectionMatrix, transformationLoc, materialLoc);
                        }





    }


    void Renderer::renderDebug(const glm::mat4x4 &modelViewMatrix, const glm::mat4x4 &projectionMatrix){
       glm::mat4x4 localprojectionMatrix = glm::perspective	( 90.f, (float)mWidth/(float)mHeight, 0.1f, 10.0f );
        glm::mat4x4 localmodelViewMatrix = g_viewMatrix;

        // 3 - Activer et paramétrer les shader par défaut :
        //    3.1 - Activer le programme par défaut
        glAssert( glUseProgram(g_programLine) );

        //    3.2 - Récupérer les adresses des paramètres
        GLint transformationLoc[4];
        glAssert( transformationLoc[0] = glGetUniformLocation(g_programLine, "modelViewMatrix") );
        glAssert( transformationLoc[1] = glGetUniformLocation(g_programLine, "projectionMatrix") );
        glAssert( transformationLoc[2] = glGetUniformLocation(g_programLine, "normalMatrix") );
        glAssert( transformationLoc[3] = glGetUniformLocation(g_programLine, "MVP") );

        //
        GLint materialLoc[4];
        glAssert( materialLoc[0] = glGetUniformLocation(g_program, "materialKd") );
        glAssert( materialLoc[1] = glGetUniformLocation(g_program, "materialKs") );
        glAssert( materialLoc[2] = glGetUniformLocation(g_program, "materialNs") );
        glAssert( materialLoc[3] = glGetUniformLocation(g_program, "baseTexture") );
        //

        GLint colorLocation;
        glAssert( colorLocation = glGetUniformLocation(g_programLine, "color") );

        g_polyline.drawGL(localmodelViewMatrix, localprojectionMatrix, transformationLoc, colorLocation);
        controlPolygon->drawGL(localmodelViewMatrix, localprojectionMatrix, transformationLoc, colorLocation);

        //Draw kd tree boxes
        /*  for (std::vector<MyGlEntity*>::iterator it = g_entities.begin(); it != g_entities.end(); ++it ) {
       (*it)->drawGL(localmodelViewMatrix, localprojectionMatrix, transformationLoc, materialLoc);
    }*/
    }




int Renderer::handleKeyEvent(char key) {

    switch (key) {
    case 'w':
        glAssert( glPolygonMode(GL_FRONT_AND_BACK, GL_LINE) );
        break;
    case 'f':
        glAssert( glPolygonMode(GL_FRONT_AND_BACK, GL_FILL) );
        break;
    }
    return 1;
}

    int Renderer::handleMouseEvent(const MouseEvent & event){
        static int modifiers = 0;
        static int x = 0;
        static int y = 0;
        static int button = 0;
        static bool moved = false;

        if (event.click) {
            x = event.x;
            y = event.y;
            button = event.button;
            modifiers = event.modifiers;
        }

        if (event.button == MouseEvent::MOVE) {
            float dx = (float)(event.x - x) / (float) mWidth;
            float dy = (float)(event.y - y) / (float) mHeight;
            switch (button) {
            case MouseEvent::LEFT : {
                    // Début du code à écrire
                    // 1 - Récupérer les axes xvec et yvec de la caméra
                    // 2 - Modifier g_viewMatrix pour y ajouter une rotation de dy*360.f degrés autour de xvec
                    // 3 - Modifier g_viewMatrix pour y ajouter une rotation de dx*360.f degrés autour de yvec
                    // Fin du code à écrire

                    // -- begin TOREMOVE-TP2
                    glm::vec3 xvec = glm::vec3(glm::row(g_viewMatrix, 0));
                    glm::vec3 yvec = glm::vec3(glm::row(g_viewMatrix, 1));
                    g_viewMatrix = glm::rotate(g_viewMatrix, dy*360.f, xvec);
                    g_viewMatrix = glm::rotate(g_viewMatrix, dx*360.f, yvec);
                    // -- end TOREMOVE-TP2
                }
                break;
            case MouseEvent::RIGHT : {
                    // Début du code à écrire
                    // 1 - Calculer les vecteurs déplacement xvec et yvec selon les axes X et Y de la caméra
                    // 2 - Modifier g_viewMatrix pour y ajouter une translation de xvec+yvec
                    // Fin du code à écrire

                    // -- begin TOREMOVE-TP2
                    glm::vec3 xvec = glm::vec3(glm::row(g_viewMatrix, 0)) * dx * 10.f;
                    glm::vec3 yvec = glm::vec3(glm::row(g_viewMatrix, 1)) * dy * 10.f;
                    glm::vec3 tvec = xvec+yvec;
                    g_viewMatrix = glm::translate(g_viewMatrix, tvec);
                    // -- end TOREMOVE-TP2
                }
                break;
            case MouseEvent::MIDDLE : {
                    // Début du code à écrire
                    // 1 - Calculer le vecteur déplacement zvec selon l'axe Z de la caméra
                    // 2 - Modifier g_viewMatrix pour y ajouter une translation de zvec
                    // Fin du code à écrire

                    // -- begin TOREMOVE-TP2
                    glm::vec3 zvec = glm::vec3(glm::row(g_viewMatrix, 2)) * dy * 10.f;
                    g_viewMatrix = glm::translate(g_viewMatrix, zvec);
                    // -- end TOREMOVE-TP2
                }
                break;
            }
            x = event.x;
            y = event.y;
            moved = true;
            return 1;
        } else {
            moved = false;
        }

        if (event.button == MouseEvent::WHEEL) {
            return 1;
        }

        if (!event.click) {
            button = 0;
            modifiers = 0;
        }

        return 1;
    }


void Renderer::setViewport(int width, int height) {
    mWidth = width;
    mHeight = height;
    glAssert( glViewport(0, 0, mWidth, mHeight) );
}

MyGLMaterial* Renderer::getMaterialById(int id){
    int max = g_mat.size()-1;
    if(id > max || id<=0){
        return g_mat[0];// default texture
    }else{
        return g_mat[id];

    }
}



} // namespace rendersystem
